{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Programming C/C++ using Pybind11\n",
    "\n",
    "In this notebook we will show how to leverage [Pybind11](https://pybind11.readthedocs.io/en/stable/intro.html) \n",
    "to develop normal C/C++ program in Jupyter environment. This is a unique feature added by the \n",
    "`pynq` package. \n",
    "\n",
    "Compared to the [SWIG](http://www.swig.org/) binding, Pybind11\n",
    "supports C++ program; therefore it is the recommended way for \n",
    "binding C/C++ programs with Python interfaces."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "\n",
       "try {\n",
       "require(['notebook/js/codecell'], function(codecell) {\n",
       "  codecell.CodeCell.options_default.highlight_modes[\n",
       "      'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n",
       "  Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
       "      Jupyter.notebook.get_cells().map(function(cell){\n",
       "          if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
       "  });\n",
       "});\n",
       "} catch (e) {};\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "\n",
       "try {\n",
       "require(['notebook/js/codecell'], function(codecell) {\n",
       "  codecell.CodeCell.options_default.highlight_modes[\n",
       "      'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n",
       "  Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
       "      Jupyter.notebook.get_cells().map(function(cell){\n",
       "          if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
       "  });\n",
       "});\n",
       "} catch (e) {};\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "\n",
       "try {\n",
       "require(['notebook/js/codecell'], function(codecell) {\n",
       "  codecell.CodeCell.options_default.highlight_modes[\n",
       "      'magic_text/x-csrc'] = {'reg':[/^%%pybind11/]};\n",
       "  Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
       "      Jupyter.notebook.get_cells().map(function(cell){\n",
       "          if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
       "  });\n",
       "});\n",
       "} catch (e) {};\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from pynq.lib import pybind11"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example 1: Simple functions\n",
    "Let's first look at a very simple example. With the magic `pybind11`,\n",
    "we can specify the desired Python module name `example1`. Under the hood:\n",
    "1. The C++ program (`example1.cpp`) is generated.\n",
    "2. The C++ program is compiled into a shared object file (`example1.cpython-36m-<arch>.so`).\n",
    "\n",
    "**The compilation process can take around 20 seconds, so please be patient.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pybind11 example1\n",
    "\n",
    "#include <iostream> \n",
    "#include <string>\n",
    "using namespace std;\n",
    "\n",
    "const string greeting = \"Hello, PYNQ.\";\n",
    "\n",
    "int multiply(int a, int b) {\n",
    "    return a*b;\n",
    "}\n",
    "\n",
    "double multiply_half(double c){\n",
    "    return c*0.5;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we can easily import the module and call the C++ functions as if they\n",
    "are available as Python functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import example1\n",
    "example1.multiply(2,3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hello, PYNQ.'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "example1.greeting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.25"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "example1.multiply_half(0.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note:** you can change the C++ program and recompile, but for Jupyter notebook\n",
    "to reload the new context, you will need to restart the kernel\n",
    "for changes to take effect.\n",
    "\n",
    "## Example 2: Classes\n",
    "\n",
    "Maybe you are tired of the simple C program, so let's look at how we deal\n",
    "with classes in C++ code. We will use [the example in Pybind11 documentation](https://pybind11.readthedocs.io/en/stable/classes.html). Compare to the original\n",
    "C++ code, all we have to do is to add the magic `%%pybind11 example2` in the\n",
    "following cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%pybind11 example2\n",
    "\n",
    "#include <iostream> \n",
    "#include <string>\n",
    "\n",
    "class Pet {\n",
    "    public:\n",
    "        Pet(const std::string &name) : name(name) { }\n",
    "        void setName(const std::string &name_) { name = name_; }\n",
    "        const std::string &getName() const { return name; }\n",
    "    private:\n",
    "        std::string name;\n",
    "};\n",
    "\n",
    "class Dog : public Pet {\n",
    "    public:\n",
    "        Dog(const std::string &name) : Pet(name) { }\n",
    "        std::string bark() const { return \"woof!\"; }\n",
    "};\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we should be able to use the class as if it is implemented in Python.\n",
    "Notice we are not binding the private attribute `name` as a property (in principle\n",
    "you should be able to do so with Pybind11); this is because it may not be clear what its \n",
    "setter and getter functions are. So we just use its setter and getter explicitly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Bob'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import example2\n",
    "p = example2.Pet('Alice')\n",
    "p.setName(\"Bob\")\n",
    "p.getName()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'woof!'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d = example2.Dog('Dave')\n",
    "d.bark()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright (C) 2020 Xilinx, Inc"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
